<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>安卓设备投屏</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
        .container { max-width: 1200px; margin: 0 auto; display: flex; gap: 30px; }
        .left-panel { flex: 1; background: white; border-radius: 15px; padding: 20px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); }
        .right-panel { flex: 1; background: white; border-radius: 15px; padding: 20px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); }
        .phone-frame { background: #000; border-radius: 20px; padding: 20px; display: inline-block; box-shadow: 0 10px 30px rgba(0,0,0,0.3); }
        #screen-canvas { border-radius: 10px; display: block; background: #000; cursor: crosshair; width: 320px; height: 640px; }
        .status { padding: 15px; margin: 20px 0; background: #f8f9fa; border-radius: 10px; border-left: 4px solid #007bff; }
        .controls { margin: 20px 0; }
        .btn { padding: 12px 24px; margin: 8px 8px 8px 0; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; }
        .btn-primary { background: #007bff; color: white; }
        .btn-danger { background: #dc3545; color: white; }
        .btn-success { background: #28a745; color: white; }
        .info-section { background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0; }
        .info-item { display: flex; justify-content: space-between; margin-bottom: 12px; padding: 8px 0; border-bottom: 1px solid #dee2e6; }
        .title { text-align: center; margin-bottom: 30px; color: #333; }
        @media (max-width: 768px) { .container { flex-direction: column; gap: 20px; } }
    </style>
</head>
<body>
    <div class="title">
        <h1>📱 安卓设备投屏</h1>
        <p>实时投屏到浏览器，支持触摸操作</p>
    </div>

    <div class="container">
        <div class="left-panel">
            <h2>📺 投屏显示</h2>
            <div class="phone-frame">
                <canvas id="screen-canvas" width="320" height="640"></canvas>
            </div>
        </div>

        <div class="right-panel">
            <h2>🎮 控制面板</h2>
            
            <div class="status">
                <div id="status-text">状态: 未连接</div>
                <div id="fps-counter">FPS: 0</div>
            </div>

            <div class="controls">
                <h3>连接控制</h3>
                <button id="start-scrcpy-btn" class="btn btn-primary">🚀 启动投屏</button>
                <button id="stop-scrcpy-btn" class="btn btn-danger" disabled>⏹️ 停止投屏</button>
                <button id="connect-btn" class="btn btn-primary">🔌 连接WebSocket</button>
                <button id="disconnect-btn" class="btn btn-danger" disabled>❌ 断开WebSocket</button>
                <button id="fullscreen-btn" class="btn btn-success">🖥️ 全屏显示</button>
                <button id="test-video-btn" class="btn btn-success">🧪 测试视频流</button>
            </div>

            <div class="info-section">
                <h3>📊 连接信息</h3>
                <div class="info-item">
                    <span>连接状态:</span>
                    <span id="connection-status">未连接</span>
                </div>
                <div class="info-item">
                    <span>投屏状态:</span>
                    <span id="scrcpy-status">未启动</span>
                </div>
                <div class="info-item">
                    <span>设备ID:</span>
                    <span id="device-id">--</span>
                </div>
                <div class="info-item">
                    <span>最后更新:</span>
                    <span id="last-update">--</span>
                </div>
            </div>

            <div class="info-section">
                <h3>💡 操作说明</h3>
                <div class="info-item">
                    <span>鼠标点击:</span>
                    <span class="info-value">模拟触摸</span>
                </div>
                <div class="info-item">
                    <span>键盘:</span>
                    <span class="info-value">发送按键</span>
                </div>
                <div class="info-item">
                    <span>滚轮:</span>
                    <span class="info-value">模拟滚动</span>
                </div>
            </div>
        </div>
    </div>

    <script>
        class AndroidMirrorClient {
            constructor() {
                this.ws = null;
                this.isConnected = false;
                this.frameCount = 0;
                this.lastFrameTime = Date.now();
                this.fps = 0;
                
                // 添加防抖
                this.touchDebounceTimer = null;
                this.touchDebounceDelay = 100; // 100ms防抖
                
                this.init();
            }

            init() {
                this.connectBtn = document.getElementById('connect-btn');
                this.disconnectBtn = document.getElementById('disconnect-btn');
                this.startScrcpyBtn = document.getElementById('start-scrcpy-btn');
                this.stopScrcpyBtn = document.getElementById('stop-scrcpy-btn');
                this.fullscreenBtn = document.getElementById('fullscreen-btn');
                this.statusText = document.getElementById('status-text');
                this.fpsCounter = document.getElementById('fps-counter');
                this.connectionStatus = document.getElementById('connection-status');
                this.scrcpyStatus = document.getElementById('scrcpy-status');
                this.deviceId = document.getElementById('device-id');
                this.lastUpdate = document.getElementById('last-update');
                this.screenCanvas = document.getElementById('screen-canvas');
                
                // 初始化canvas显示
                this.initCanvas();
                
                this.bindEvents();
                this.startFPSCounter();
            }

            initCanvas() {
                if (this.screenCanvas) {
                    const ctx = this.screenCanvas.getContext('2d');
                    
                    // 设置背景色
                    ctx.fillStyle = '#2c3e50';
                    ctx.fillRect(0, 0, this.screenCanvas.width, this.screenCanvas.height);
                    
                    // 显示等待信息
                    ctx.fillStyle = '#ffffff';
                    ctx.font = '24px Arial';
                    ctx.textAlign = 'center';
                    ctx.fillText('等待投屏...', this.screenCanvas.width / 2, this.screenCanvas.height / 2);
                    
                    ctx.font = '16px Arial';
                    ctx.fillText('请先连接WebSocket并启动投屏', this.screenCanvas.width / 2, this.screenCanvas.height / 2 + 40);
                    
                    console.log('Canvas初始化完成');
                }
            }

            bindEvents() {
                this.connectBtn.addEventListener('click', () => this.connect());
                this.disconnectBtn.addEventListener('click', () => this.disconnect());
                this.startScrcpyBtn.addEventListener('click', () => this.startScrcpy());
                this.stopScrcpyBtn.addEventListener('click', () => this.stopScrcpy());
                this.fullscreenBtn.addEventListener('click', () => this.toggleFullscreen());
                
                document.addEventListener('keydown', (e) => this.handleKey(e));
                
                // 确保canvas事件正确绑定
                if (this.screenCanvas) {
                    console.log('绑定canvas事件监听器');
                    this.screenCanvas.addEventListener('click', (e) => this.handleCanvasClick(e));
                    this.screenCanvas.addEventListener('mousemove', (e) => this.handleCanvasMouseMove(e));
                    this.screenCanvas.addEventListener('wheel', (e) => this.handleCanvasWheel(e));
                    
                    // 添加触摸反馈
                    this.screenCanvas.style.cursor = 'crosshair';
                } else {
                    console.error('Canvas元素未找到');
                }
            }

            startScrcpy() {
                if (!this.isConnected) {
                    alert('请先连接WebSocket');
                    return;
                }

                this.startScrcpyBtn.disabled = true;
                this.stopScrcpyBtn.disabled = false;
                this.updateScreenStatus('正在启动投屏...', 'info');

                const scrcpyConfig = {
                    type: 'start_scrcpy',
                    port: 27183,
                    maxSize: 1080,
                    bitrate: 8000000,
                    tunnelHost: '127.0.0.1'
                };

                this.ws.send(JSON.stringify(scrcpyConfig));
            }

            stopScrcpy() {
                if (this.ws && this.ws.readyState === WebSocket.OPEN) {
                    this.ws.send(JSON.stringify({ type: 'stop_scrcpy' }));
                }
                
                this.startScrcpyBtn.disabled = false;
                this.stopScrcpyBtn.disabled = true;
                this.updateScreenStatus('投屏已停止', 'warning');
            }

            connect() {
                this.updateStatus('正在连接...', false);
                this.updateScreenStatus('正在连接WebSocket...', 'info');
                
                try {
                    this.ws = new WebSocket('ws://localhost:3000');
                    
                    this.ws.onopen = () => {
                        this.isConnected = true;
                        this.updateStatus('已连接', true);
                        this.updateButtons(true);
                        this.connectionStatus.textContent = '已连接';
                        this.lastUpdate.textContent = new Date().toLocaleTimeString();
                        this.updateScreenStatus('WebSocket已连接，请启动投屏', 'success');
                    };

                    this.ws.onmessage = (event) => {
                        this.handleMessage(event.data);
                    };

                    this.ws.onclose = () => {
                        this.isConnected = false;
                        this.updateStatus('连接已断开', false);
                        this.updateButtons(false);
                        this.connectionStatus.textContent = '已断开';
                        this.updateScreenStatus('WebSocket连接已断开', 'error');
                    };

                    this.ws.onerror = (error) => {
                        console.error('WebSocket错误:', error);
                        this.updateStatus('连接失败', false);
                        this.updateScreenStatus('WebSocket连接失败', 'error');
                    };

                } catch (error) {
                    console.error('连接失败:', error);
                    this.updateStatus('连接失败', false);
                    this.updateScreenStatus('连接失败', 'error');
                }
            }

            disconnect() {
                if (this.ws) {
                    this.ws.close();
                }
                this.isConnected = false;
                this.updateStatus('已断开', false);
                this.updateButtons(false);
                this.connectionStatus.textContent = '已断开';
                this.updateScreenStatus('等待连接设备...', 'info');
            }

            handleMessage(data) {
                try {
                    const message = JSON.parse(data);
                    
                    switch (message.type) {
                        case 'video_frame':
                            this.displayVideoFrame(message);
                            break;
                        case 'scrcpy_status':
                            this.handleScrcpyStatus(message);
                            break;
                        case 'scrcpy_response':
                            this.handleScrcpyResponse(message);
                            break;
                        case 'status':
                            console.log('状态消息:', message.message);
                            break;
                        default:
                            console.log('消息类型:', message.type);
                    }
                } catch (error) {
                    console.error('解析消息失败:', error);
                }
            }

            displayVideoFrame(message) {
                try {
                    console.log('收到视频帧:', message.type, '大小:', message.width, 'x', message.height);
                    const canvas = this.screenCanvas;
                    const ctx = canvas.getContext('2d');
                    
                    if (message.data.startsWith('data:video/h264')) {
                        // H.264视频流，需要特殊处理
                        console.log('收到H.264视频流');
                        
                        // 创建video元素来解码H.264
                        if (!this.videoElement) {
                            this.videoElement = document.createElement('video');
                            this.videoElement.style.display = 'none';
                            this.videoElement.autoplay = true;
                            this.videoElement.muted = true;
                            document.body.appendChild(this.videoElement);
                        }
                        
                        // 设置视频源
                        this.videoElement.src = message.data;
                        
                        // 当视频可以播放时，绘制到canvas
                        this.videoElement.oncanplay = () => {
                            // 清空canvas
                            ctx.clearRect(0, 0, canvas.width, canvas.height);
                            
                            // 绘制视频帧到canvas
                            ctx.drawImage(this.videoElement, 0, 0, canvas.width, canvas.height);
                            
                            // 更新信息
                            this.lastUpdate.textContent = new Date().toLocaleTimeString();
                            
                            // 计算FPS
                            this.frameCount++;
                            const now = Date.now();
                            if (now - this.lastFrameTime >= 1000) {
                                this.fps = this.frameCount;
                                this.frameCount = 0;
                                this.lastFrameTime = now;
                            }
                            
                            console.log('H.264视频帧显示完成，FPS:', this.fps);
                        };
                        
                    } else {
                        // 普通图像数据
                        const img = new Image();
                        img.onload = () => {
                            console.log('图像加载成功，尺寸:', img.width, 'x', img.height);
                            
                            // 清空canvas
                            ctx.clearRect(0, 0, canvas.width, canvas.height);
                            
                            // 绘制图像到canvas
                            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
                            
                            // 更新信息
                            this.lastUpdate.textContent = new Date().toLocaleTimeString();
                            
                            // 计算FPS
                            this.frameCount++;
                            const now = Date.now();
                            if (now - this.lastFrameTime >= 1000) {
                                this.fps = this.frameCount;
                                this.frameCount = 0;
                                this.lastFrameTime = now;
                            }
                        };
                        
                        img.src = message.data;
                    }
                    
                } catch (error) {
                    console.error('显示视频帧失败:', error);
                }
            }

            handleScrcpyStatus(message) {
                console.log('收到scrcpy状态:', message);
                
                switch (message.status) {
                    case 'connected':
                        this.connectionStatus.textContent = '设备已连接';
                        this.updateStatus('投屏已启动', true);
                        this.scrcpyStatus.textContent = '已启动';
                        this.deviceId.textContent = message.deviceId || 'N/A';
                        this.updateScreenStatus('投屏已启动，可以操作设备', 'success');
                        break;
                    case 'disconnected':
                        this.connectionStatus.textContent = '设备已断开';
                        this.updateStatus('投屏已停止', false);
                        this.scrcpyStatus.textContent = '已停止';
                        this.deviceId.textContent = 'N/A';
                        this.updateScreenStatus('设备已断开', 'warning');
                        break;
                    case 'error':
                        this.connectionStatus.textContent = `错误: ${message.message}`;
                        this.updateStatus('投屏错误', false);
                        this.scrcpyStatus.textContent = '错误';
                        this.deviceId.textContent = 'N/A';
                        this.updateScreenStatus(`投屏错误: ${message.message}`, 'error');
                        break;
                    case 'stopped':
                        this.connectionStatus.textContent = '投屏已停止';
                        this.updateStatus('投屏已停止', false);
                        this.scrcpyStatus.textContent = '已停止';
                        this.deviceId.textContent = 'N/A';
                        this.startScrcpyBtn.disabled = false;
                        this.stopScrcpyBtn.disabled = true;
                        this.updateScreenStatus('投屏已停止', 'warning');
                        break;
                }
            }

            handleScrcpyResponse(message) {
                if (message.success) {
                    console.log('Scrcpy操作成功:', message.message);
                    if (message.message.includes('启动成功')) {
                        this.updateScreenStatus('投屏启动成功', 'success');
                    }
                } else {
                    console.error('Scrcpy操作失败:', message.message);
                    this.updateScreenStatus(`投屏失败: ${message.message}`, 'error');
                    this.startScrcpyBtn.disabled = false;
                    this.stopScrcpyBtn.disabled = true;
                }
            }

            updateScreenStatus(message, type) {
                // This function is no longer needed for canvas display,
                // as the canvas itself handles the status message and color.
                // Keeping it for now, but it will be removed if not used elsewhere.
                if (this.screenStatus) {
                    this.screenStatus.textContent = message;
                    
                    // 根据类型设置颜色
                    const colors = {
                        'info': '#3498db',
                        'success': '#27ae60',
                        'warning': '#f39c12',
                        'error': '#e74c3c'
                    };
                    
                    this.screenStatus.style.color = colors[type] || '#888';
                }
            }

            handleKey(event) {
                if (!this.isConnected) return;
                this.sendKeyEvent(event.key, event.keyCode);
            }

            sendKeyEvent(key, keyCode) {
                if (this.ws && this.ws.readyState === WebSocket.OPEN) {
                    this.ws.send(JSON.stringify({
                        type: 'key',
                        key: key,
                        keyCode: keyCode,
                        timestamp: Date.now()
                    }));
                }
            }

            updateStatus(text, connected) {
                this.statusText.textContent = `状态: ${text}`;
            }

            updateButtons(connected) {
                this.connectBtn.disabled = connected;
                this.disconnectBtn.disabled = !connected;
                this.startScrcpyBtn.disabled = !connected;
                this.stopScrcpyBtn.disabled = true;
            }

            startFPSCounter() {
                setInterval(() => {
                    this.fpsCounter.textContent = `FPS: ${this.fps}`;
                }, 1000);
            }

            toggleFullscreen() {
                const screenCanvas = document.getElementById('screen-canvas');
                if (!document.fullscreenElement) {
                    screenCanvas.requestFullscreen().catch(err => {
                        console.error('全屏失败:', err);
                    });
                } else {
                    document.exitFullscreen();
                }
            }

            handleCanvasClick(event) {
                if (!this.isConnected) {
                    console.log('WebSocket未连接，无法发送触摸事件');
                    return;
                }
                
                const rect = this.screenCanvas.getBoundingClientRect();
                const x = Math.floor((event.clientX - rect.left) * (this.screenCanvas.width / rect.width));
                const y = Math.floor((event.clientY - rect.top) * (this.screenCanvas.height / rect.height));
                
                console.log('发送触摸事件:', { x, y, action: 'down' });
                this.sendTouchEvent(x, y, 'down');
            }

            handleCanvasMouseMove(event) {
                if (!this.isConnected) return;
                
                // 防抖处理
                if (this.touchDebounceTimer) {
                    clearTimeout(this.touchDebounceTimer);
                }
                
                this.touchDebounceTimer = setTimeout(() => {
                    const rect = this.screenCanvas.getBoundingClientRect();
                    const x = Math.floor((event.clientX - rect.left) * (this.screenCanvas.width / rect.width));
                    const y = Math.floor((event.clientY - rect.top) * (this.screenCanvas.height / rect.height));
                    
                    this.sendTouchEvent(x, y, 'move');
                }, this.touchDebounceDelay);
            }

            handleCanvasWheel(event) {
                if (!this.isConnected) return;
                event.preventDefault();
                
                // 防抖处理
                if (this.touchDebounceTimer) {
                    clearTimeout(this.touchDebounceTimer);
                }
                
                this.touchDebounceTimer = setTimeout(() => {
                    const rect = this.screenCanvas.getBoundingClientRect();
                    const x = Math.floor((event.clientX - rect.left) * (this.screenCanvas.width / rect.width));
                    const y = Math.floor((event.clientY - rect.top) * (this.screenCanvas.height / rect.height));
                    
                    this.sendTouchEvent(x, y, 'scroll');
                }, this.touchDebounceDelay);
            }

            sendTouchEvent(x, y, action) {
                if (this.ws && this.ws.readyState === WebSocket.OPEN) {
                    const touchData = {
                        type: 'touch',
                        action: action,
                        x: x,
                        y: y,
                        timestamp: Date.now()
                    };
                    
                    console.log('发送触摸数据:', touchData);
                    this.ws.send(JSON.stringify(touchData));
                } else {
                    console.error('WebSocket未连接或未就绪');
                }
            }
        }

        document.addEventListener('DOMContentLoaded', () => {
            new AndroidMirrorClient();
        });
    </script>
</body>
</html> 